// TravelRouteServiceImpl.java
package org.example.travel.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.travel.dto.RouteActionDTO;
import org.example.travel.dto.TravelRouteSearchDTO;
import org.example.travel.entity.RouteLog;
import org.example.travel.entity.TravelRoute;
import org.example.travel.mapper.RouteLogMapper;
import org.example.travel.mapper.TravelRouteMapper;
import org.example.travel.service.TravelRouteService;
import org.example.travel.common.UserHolder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
public class TravelRouteServiceImpl implements TravelRouteService {

    private final TravelRouteMapper travelRouteMapper;
    private final RouteLogMapper routeLogMapper;
    private final StringRedisTemplate redisTemplate;
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public IPage<TravelRoute> search(TravelRouteSearchDTO dto) {
        QueryWrapper<TravelRoute> query = new QueryWrapper<>();
        extracted(dto, query);
        query.orderByDesc("heat_score");
        Page<TravelRoute> page = new Page<>(dto.getPage(), dto.getSize());
        return travelRouteMapper.selectPage(page, query);
    }

    private static void extracted(TravelRouteSearchDTO dto, QueryWrapper<TravelRoute> query) {
        if (StringUtils.hasText(dto.getKeyword())) {
            query.like("name", dto.getKeyword());
        }
    }

    @Override
    public void logAction(RouteActionDTO dto) {
        Long userId = UserHolder.getUserId();
        RouteLog log = new RouteLog();
        log.setUserId(userId);
        log.setRouteId(dto.getRouteId());
        log.setActionType(dto.getActionType());
        log.setCreateTime(LocalDateTime.now());
        try {
            String json = objectMapper.writeValueAsString(log);
            redisTemplate.opsForList().rightPush("route:log", json);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("写入路线行为日志失败", e);
        }
    }

    @Override
    public void flushRouteLogs() {
        List<String> logs = redisTemplate.opsForList().range("route:log", 0, -1);
        if (logs == null || logs.isEmpty()) return;

        List<RouteLog> parsedLogs = logs.stream().map(json -> {
            try {
                return objectMapper.readValue(json, RouteLog.class);
            } catch (JsonProcessingException e) {
                return null;
            }
        }).filter(Objects::nonNull).collect(Collectors.toList());

        parsedLogs.forEach(routeLogMapper::insert);
        redisTemplate.delete("route:log");
    }

    @Override
    public void updateHeatScoresAndCache() {
        LocalDateTime cutoff = LocalDateTime.now().minusDays(1);
        QueryWrapper<RouteLog> query = new QueryWrapper<>();
        query.ge("create_time", cutoff);
        List<RouteLog> logs = routeLogMapper.selectList(query);

        Map<Long, List<RouteLog>> logMap = logs.stream()
                .collect(Collectors.groupingBy(RouteLog::getRouteId));

        List<Long> routeIds = new ArrayList<>(logMap.keySet());
        List<TravelRoute> routeList = travelRouteMapper.selectBatchIds(routeIds);

        for (TravelRoute route : routeList) {
            List<RouteLog> routeLogs = logMap.getOrDefault(route.getRouteId(), Collections.emptyList());
            long view = routeLogs.stream().filter(l -> l.getActionType() == 0).count();
            long collect = routeLogs.stream().filter(l -> l.getActionType() == 1).count();
            route.setView(route.getView() + view);
            route.setCollect(route.getCollect() + collect);
            route.setHeatScore(route.getView() * 0.4 + route.getCollect() * 0.6);
            travelRouteMapper.updateById(route);
        }

        List<TravelRoute> all = travelRouteMapper.selectList(null);
        List<TravelRoute> top20 = all.stream()
                .sorted(Comparator.comparingDouble(TravelRoute::getHeatScore).reversed())
                .limit(20).collect(Collectors.toList());

        try {
            String json = objectMapper.writeValueAsString(top20);
            int ttl = 86400 + new Random().nextInt(600);
            redisTemplate.opsForValue().set("hot:route", json, ttl, TimeUnit.SECONDS);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("热门路线缓存失败", e);
        }
    }
}
